package com.wmx.hb.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * AOP 切面类->环绕通知 终止目标方法执行
 * 1、比如某个方法只有管理员才有权限执行，而普通用户是没有权限
 * 2、比如不符合条件的时候，需要终止(跳过)目标方法的执行
 * 3、比如一个组件(Component)专门用于做校验，里面的方法是否校验可以配置在数据库中，当配置为启用时，则继续校验，否则不校验。
 *
 * @author Think
 */
@Aspect
@Order(value = 999)
@Component
@SuppressWarnings("All")
public class BasicDataRuleAop {

    private static final Logger log = LoggerFactory.getLogger(BasicDataRuleAop.class);

    /**
     * @Pointcut ：切入点声明：针对 编辑、变更、保存并送审方法进行拦截
     */
    @Pointcut(value = "execution(* com.wmx.hb.mapper.EmpMapper.*(..))")
    private void aspectPointcut() {

    }

    /**
     * 环绕通知
     * 1、@Around 的 value 属性：绑定通知的切入点表达式。可以关联切入点声明，也可以直接设置切入点表达式
     * 2、Object ProceedingJoinPoint.proceed(Object[] args) 方法：继续下一个通知或目标方法调用，返回处理结果，如果目标方法发生异常，则 proceed() 会抛异常.
     * 3、假如目标方法是控制层接口，则本方法的异常捕获与否都不会影响业务层方法的事务回滚
     * 4、假如目标方法是控制层接口，本方法 try-catch 了异常后没有继续往外抛，则全局异常处理 @RestControllerAdvice 中不会再触发
     */
    @Around(value = "aspectPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Object target = joinPoint.getTarget();
        System.out.println("环绕通知=" + methodSignature.getName() + " " + methodSignature);
        System.out.println("环绕通知=" + target);

        // 是否继续校验
        boolean validation = true;
        if (validation) {
            // 校验通过后执行目标方法
            // 继续下一个切面通知或目标方法调用，返回处理结果，如果目标方法发生异常，则 proceed 会抛异常.
            // 如果在调用目标方法或者下一个切面通知前抛出异常，则不会再继续往后走
            return joinPoint.proceed(joinPoint.getArgs());
        } else {
            // 校验未通过时，不继续往后走，直接返回。
            // 可以返回提示信息，但是必须保证返回的参数类型与目标方法的返回值类型一致，否则类型转换异常。
            // 也可以直接抛异常。
            return null;
        }
    }


}
